<div class="wrapper">
  <canvas id="glcanvas" ref="webgl" width="375" height="667"></canvas>
</div>

<script>
  /**
   * Create a program object and make current
   * @param gl GL context
   * @param vshader a vertex shader program (string)
   * @param fshader a fragment shader program (string)
   * @return true, if the program object was created and successfully made current
   */
  function initShaders(gl, vshader, fshader) {
    var program = createProgram(gl, vshader, fshader);
    if (!program) {
      console.log("无法创建程序对象");
      return false;
    }

    gl.useProgram(program);
    gl.program = program;

    return true;
  }

  /**
   * Create the linked program object
   * @param gl GL context
   * @param vshader a vertex shader program (string)
   * @param fshader a fragment shader program (string)
   * @return created program object, or null if the creation has failed
   */
  function createProgram(gl, vshader, fshader) {
    // 创建着色器对象
    var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
    var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
    if (!vertexShader || !fragmentShader) {
      return null;
    }

    // 创建程序对象
    var program = gl.createProgram();
    if (!program) {
      return null;
    }

    // 为程序对象分配顶点着色器和片元着色器
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);

    // 连接着色器
    gl.linkProgram(program);

    // 检查连接
    var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
    if (!linked) {
      var error = gl.getProgramInfoLog(program);
      console.log("无法连接程序对象: " + error);
      gl.deleteProgram(program);
      gl.deleteShader(fragmentShader);
      gl.deleteShader(vertexShader);
      return null;
    }
    return program;
  }

  /**
   * 创建着色器对象
   * @param gl GL context
   * @param type the type of the shader object to be created
   * @param source shader program (string)
   * @return created shader object, or null if the creation has failed.
   */
  function loadShader(gl, type, source) {
    // 创建着色器对象
    var shader = gl.createShader(type);
    if (shader == null) {
      console.log("无法创建着色器");
      return null;
    }

    // 设置着色器源代码
    gl.shaderSource(shader, source);

    // 编译着色器
    gl.compileShader(shader);

    // 检查着色器的编译状态
    var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if (!compiled) {
      var error = gl.getShaderInfoLog(shader);
      console.log("Failed to compile shader: " + error);
      gl.deleteShader(shader);
      return null;
    }

    return shader;
  }




  let VSHADER_SOURCE = `
        attribute vec4 a_Position;
        attribute vec2 a_TexCoord;
        varying vec2 v_TexCoord;

        void main() {
          gl_Position = a_Position;
          v_TexCoord = a_TexCoord;
        }`;
  let FSHADER_SOURCE = `
        precision mediump float;
        uniform sampler2D u_Sampler;
        uniform float time;
        varying vec2 v_TexCoord;

        float noise(vec3 p){
            vec3 i = floor(p);
            vec4 a = dot(i, vec3(1., 57., 21.)) + vec4(0., 57., 21., 78.);
            vec3 f = cos((p-i)*acos(-1.))*(-.5)+.5;
            a = mix(sin(cos(a)*a),sin(cos(1.+a)*(1.+a)), f.x);
            a.xy = mix(a.xz, a.yw, f.y);
            return mix(a.x, a.y, f.z);
        }

        float sphere(vec3 p, vec4 spr){
            return length(spr.xyz-p) - spr.w;
        }

        float flame(vec3 p){
            float d = sphere(p*vec3(1.,.5,1.), vec4(.0,-1.,.0,1.));
            return d + (noise(p+vec3(.0,time*2.,.0)) + noise(p*3.)*.5)*.25*(p.y) ;
        }

        float scene(vec3 p){
            return min(100.-length(p) , abs(flame(p)) );
        }

        vec4 raymarch(vec3 org, vec3 dir){
            float d = 0.0, glow = 0.0, eps = 0.02;
            vec3  p = org;
            bool glowed = false;
            
            for(int i=0; i<64; i++)
            {
                d = scene(p) + eps;
                p += d * dir;
                if( d>eps )
                {
                    if(flame(p) < .0)
                        glowed=true;
                    if(glowed)
                        glow = float(i)/64.;
                }
            }
            return vec4(p,glow);
        }

        void main() {
            vec2 v = -1.5 + 3. * v_TexCoord;
            
            vec3 org = vec3(0., -2., 4.); 
            vec3 dir = normalize(vec3(v.x*1.6, -v.y, -1.5));
            
            vec4 p = raymarch(org, dir);
            float glow = p.w;
            
            vec4 col = mix(vec4(1.,.5,.1,1.), vec4(0.1,.5,1.,1.), p.y*.02+.4);
            
            gl_FragColor = mix(vec4(0.), col, pow(glow*2.,4.));       
        }`;

  let canvas = document.getElementById("glcanvas");

  //   let gl = getWebGLContext(canvas);
  let gl = canvas.getContext("webgl");

  initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE);

  let n = initVertexBuffers(gl);

  inirTextures(gl, n);

  let u_time = gl.getUniformLocation(gl.program, "time");

  let newTime = 0.1;
  let draw = function () {
    newTime = newTime + 0.05;
    gl.uniform1f(u_time, newTime);
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    requestAnimationFrame(draw);
  };

  draw();

  function initVertexBuffers(gl) {
    var verticesTexCoords = new Float32Array([
      -1.0, 1.0, 0.0, 1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0,
      1.0, 0.0,
    ]);
    var n = 4;
    var vertexCoordBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexCoordBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);

    var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;

    var a_Position = gl.getAttribLocation(gl.program, "a_Position");
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);
    gl.enableVertexAttribArray(a_Position);

    var a_TexCoord = gl.getAttribLocation(gl.program, "a_TexCoord");
    gl.vertexAttribPointer(
      a_TexCoord,
      2,
      gl.FLOAT,
      false,
      FSIZE * 4,
      FSIZE * 2
    );
    gl.enableVertexAttribArray(a_TexCoord);
    return n;
  }
  function inirTextures(gl, n) {
    var texture = gl.createTexture();
    var u_Sampler = gl.getUniformLocation(gl.program, "u_Sampler");
    var image = new Image();
    image.onload = () => {
      loadTexture(gl, n, texture, u_Sampler, image);
    };
    image.crossOrigin = "anonymous";
    image.src =
      "https://mp-3412f62e-9321-4f4b-918a-03f9224b13bf.cdn.bspapp.com/cloudstorage/6558d9ae-ed13-4fa6-b913-91f3eaee8a64.png";
    return true;
  }
  function loadTexture(gl, n, texture, u_Sampler, image) {
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
    gl.uniform1i(u_Sampler, 0);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
  }
</script>
<style>
    *{
        margin: 0;
        padding: 0;
    }
    .wrapper{
        width: 100vw;
        height: 100vh;
        display: flex;
        align-items: center;
        justify-content: center;
        background-color: #000;
    }
  #glcanvas {
    background-color: #000;
  }
</style>
